查看原文
其他

记一次自动翻译导致的crash

Zoey 豆皮范儿
2024-09-11

点击上方蓝字,关于我们


大家好,我们又见面了,这次我们的Zoey同学发现了一个页面崩溃的诡异问题,经过抽丝剥茧的探索,终于找出了原因。

正文共:2720字 16图

预计阅读时间:7分钟

起因

接到一个反馈说用 lark 打开公司的一个 survey 系统页面会概率性白屏,但是又不能稳定复现。

接到一个反馈说lark打开survey的h5页面后,点击第8道的选项(该题目的选中项影响后续题目的展示),概率性白屏

排查过程

•1、pc打开没问题,手机原生浏览器打开没问题,初步判断是lark webview有额外操作•2、通过错误监控系统定位到报错•(1) HierarchyRequestError: Failed to execute 'appendChild' on 'Node': The new child element contains the parent.•(2)看起来是react源码报错,感觉操作了dom•3、让用户用后门挂上vconsole,想看看堆栈,但是没有复现,用户说“概率性”发生,时好时坏•4、我也挂上vconsole本机调试,无法复现bug但是发现加载页面后log完全不同,用户的log看起来lark进行了自动翻译⬇️

•5、用户关闭手机 App 自动翻译后果然好了,至于“概率性”发生是因为,用户开启自动翻译成中文,而页面本身就是中文,App 语言检测存在缺陷,导致有时会依然将中文翻译成中文。如果我开启自动翻译为日文,就变成了稳定复现。所以究竟为什么报错呢!

报错原因

•1、盲猜 App 的翻译是类google的(经 App 同学确认后,的确是的,此处额外感谢oncall同学的hotfix),所以我用google自动翻译试了一下,果然点了第8题的选项就crash

•2、再看看google是怎么实现的翻译,检测TEXT_NODE然后用font包裹替换的方式⬇️

•3、回到复现路径和报错信息,点击第8道的选项 = 'removeChild' 报错,说明在点击选项后,react元素的可见发生了变化,而'removeChild' 报错说明react保存的父节点已经找不到原本的被翻译前的子节点,可能是因为原本的纯文本节点被套了font,尝试复现https://codesandbox.io/s/unruffled-cloud-8qech?file=/src/App.js:

export default function App() { const [showText, handleShow] = useState(true); useEffect(() => { const nodes = document.getElementById("App").childNodes; for (const childNode of nodes) { // auto translating if (childNode.nodeType === Node.TEXT_NODE) { const fontNode = document.createElement("font"); // pretend that 'Hello CodeSandbox' has been translated fontNode.textContent = childNode.data.toUpperCase(); childNode.parentElement.insertBefore(fontNode, childNode); childNode.parentElement.removeChild(childNode); } } }, []); return ( <div id="App"> {showText && "Hello CodeSandbox"} <button onClick={(e) => handleShow(false)}>Click to hide</button> <h2>Start editing to see some magic happen!</h2> </div> );}

•4、道理我懂了,开始找HTML中裸露的纯文本吧。因为每个题目区域都是conditionally render, 所以里面的每个部分都可类比上面的"HELLO CODESANDBOX", 所以筛选原则就是⬇️•nodeType = TEXT_NODE不是父节点的唯一一个子节点

function findPureTextNodes() { return $$('*').reduce((acc, node) => { if (node.childNodes) { const nodes = Array.from(node.childNodes); return acc.concat(nodes.length > 1 ? (nodes.find(child => child.nodeType === Node.TEXT_NODE) || []) : [] ) } return acc; }, []) .map(node => node.data) .filter(text => !!text); }

•5、🌚 这一堆0,懂了,你们这些该死的题号......

解决方案

•一开始的hotfix本想直接添加attribute,因为根据https://cloud.google.com/translate/troubleshooting, google能通过给html加标记来禁止翻译,但是lark没有实现这个功能⬇️• <span translate="no"> </span>• <span class="notranslate"> </span>• 捋清了crash逻辑后,给裸露纯文本包一下span就好了,debug一年,修复一分钟。。撒花结束


• 问题解决了,不过增加了多余的dom,因为题号只是想小于10的时候补0,所以我们还可以:


The     End

如果你觉得这篇文章对你有帮助,有启发,我想请你帮我2个小忙:

1、点个「在看」,让更多的人也能看到这篇文章内容;

2、关注公众号「豆皮范儿」,公众号后台回复「加群」 加入我们一起学习;


关注公众号的福利持续更新,公众号后台送学习资料:

1、豆皮范儿后台回复「vis」,还可以获取更多可视化免费学习资料。

2、豆皮范儿后台回复「webgl」,还可以获取webgl免费学习资料。

3、豆皮范儿后台回复「算法」,还可以获取算法的学习资料。

4、豆皮范儿后台回复「招聘」,获取各种内推。


字节跳动数据平台前端团队,在公司内负责大数据相关产品的研发。我们在前端技术上保持着非常强的热情,除了数据产品相关的研发外,在数据可视化、海量数据处理优化、web excel、WebIDE、私有化部署、工程工具都方面都有很多的探索和积累,有兴趣可以与我们联系。

投递简历,更多精彩文章,欢迎关注 “豆皮范儿” 


点个赞,证明你还爱我

继续滑动看下一个
豆皮范儿
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存